﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading; 
using System.Collections;

using log4net;

using NetworkAssetManager.Network;
using NetworkAssetManager.General; 
using NetworkAssetManager.DataAccess;
using NetworkAssetManager.Entity;
using NetworkAssetManager.WMI; 

//TODO_LOW  : Comments and debug statements
//TODO_HIGH : Check the db connections in this class. They are not proper right now. 
//TODO_LOW  : Naming conventions standard needs to be followed. 

namespace NetworkAssetManager
{
    class Controller 
    {
        static readonly object _padlock = new object();
        private static readonly ILog _logger = LogManager.GetLogger(typeof(Controller));
        static Controller _controller = null;
        private NetworkHelper _netHelper = null;
        private DiscoverDb disDB = null;
        private NetworkAssetManager.General.ThreadPool tp1 = null;

        delegate void ThreadDelegate(object param);

        #region Delegates and events for NetworkHelper

        public delegate void DelegateMachineDiscoveryStart(Object sender, DiscoveryDetailsArgs args);
        public event DelegateMachineDiscoveryStart MachineDiscoveryStart;

        public delegate void DelegateMachineFound(Object sender, MachineDetailsArgs args);
        public event DelegateMachineFound MachineFound;

        public delegate void DelegateMachineDiscoveryComplete(Object sender, EventArgs args);
        public event DelegateMachineDiscoveryComplete MachineDiscoveryComplete;

        public delegate void DelegateDomainDiscoveryStart(Object sender, DiscoveryDetailsArgs args);
        public event DelegateDomainDiscoveryStart DomainDiscoveryStart;

        public delegate void DelegateDomainFound(Object sender, DomainDetailsArgs args);
        public event DelegateDomainFound DomainFound;

        public delegate void DelegateDomainDiscoveryComplete(Object sender, EventArgs args);
        public event DelegateDomainDiscoveryComplete DomainDiscoveryComplete;

        public delegate void DelegateStatusMessage(Object sender, string message);
        public event DelegateStatusMessage StatusMessage;

        public delegate void DelegateScanComplete(Object sender, string machineName);
        public event DelegateScanComplete ScanComplete;


        #endregion

        /// <summary>
        /// Private _controller so that object cannot be created directly
        /// </summary>
        private Controller()
        {
            _netHelper = new NetworkHelper();
            _netHelper.MachineDiscoveryStart += new NetworkHelper.DelegateMachineDiscoveryStart(netHelper_MachineDiscoveryStart);
            _netHelper.MachineFound += new NetworkHelper.DelegateMachineFound(netHelper_MachineFound);
            _netHelper.MachineDiscoveryComplete += new NetworkHelper.DelegateMachineDiscoveryComplete(netHelper_MachineDiscoveryComplete);

            _netHelper.DomainDiscoveryStart += new NetworkHelper.DelegateDomainDiscoveryStart(netHelper_DomainDiscoveryStart);
            _netHelper.DomainFound += new NetworkHelper.DelegateDomainFound(netHelper_DomainFound);
            _netHelper.DomainDiscoveryComplete += new NetworkHelper.DelegateDomainDiscoveryComplete(netHelper_DomainDiscoveryComplete);

            tp1 = new NetworkAssetManager.General.ThreadPool(5, 20, "pool1");
            tp1.Priority = System.Threading.ThreadPriority.AboveNormal;
            tp1.NewThreadTrigger = 250;
            tp1.DynamicThreadDecay = 5000;
            tp1.Start();
            _logger.Info("Creating the controller object");
        }

        /// <summary>
        /// Property that exposes the creation of new controler instance
        /// </summary>
        public static Controller Instance
        {
            get
            {
                _logger.Info("Getting the instance of the _controller");

                lock (_padlock)
                {
                    if (_controller == null)
                    {
                        _logger.Info("Controller instance is null so creating new ");
                        _controller = new Controller();
                    }
                }
                return _controller;
            }
        }

        /// <summary>
        /// Gets the machines from the specified domain
        /// </summary>
        /// <param name="domainName">Domain from which machines needs to be discovered</param>
        public void GetMachines(string domainName)
        {
            _logger.Info("Getting the list of machines from domain "+ domainName);
            CredentialDB _credDb = new CredentialDB();
            _netHelper.Credentials = _credDb.GetAllCredentials();
            _credDb.CloseConnection();
            _credDb = null; 
            _netHelper.DiscoverMachines(SearchType.Machines, domainName);
        }

        /// <summary>
        /// Gets the machines from the specified domain
        /// </summary>
        /// <param name="domainName">Domain from which machines needs to be discovered</param>
        public void GetMachinesByHostName(string machineName)
        {
            _logger.Info("Getting machines by hostname");
            CredentialDB _credDb = new CredentialDB();
            _netHelper.Credentials = _credDb.GetAllCredentials();
            _credDb.CloseConnection();
            _credDb = null;
            _netHelper.GetMachinesByName(machineName); 
        }

        /// <summary>
        /// Gets the list of available domains 
        /// </summary>
        public void GetDomains()
        {
            _logger.Info("Getting the list of available domains");
            _netHelper.DiscoverMachines(SearchType.Domain, null);
        }

        void netHelper_MachineDiscoveryStart(object sender, DiscoveryDetailsArgs args)
        {
            _logger.Info("Machine discovery started");
            disDB = new DiscoverDb(); 
            if ( MachineDiscoveryStart!=null)
            {
                MachineDiscoveryStart(this, null); 
            }
        }

        /// <summary>
        /// function called when the Machine found event is raised by the nethelper
        /// </summary>
        /// <param name="sender">Nethelper object</param>
        /// <param name="args">MachineDetailsArgs class contains machine details</param>
        private void netHelper_MachineFound(object sender, MachineDetailsArgs args)
        {
            _logger.Info("Found machine "+args.MachineName);
            EntDiscover objDbMachine = new EntDiscover();
            objDbMachine.DomainName = args.DomainName;
            objDbMachine.MachineName = args.MachineName;
            objDbMachine.IPAddr = args.IPAddress;
            objDbMachine.CredentialID = args.CredentialID;
            objDbMachine.StatusMessage = args.StatusMessage;
            objDbMachine.Discovered = args.Discovered;
            FireStatusMessage("Found machine: " + args.MachineName); 
            if (disDB.InsertDiscover(objDbMachine) == DBCode.Ok)
            {
                if (MachineFound != null)
                {
                    MachineFound(this, args);
                }
            }
            objDbMachine = null; 
        }

        /// <summary>
        /// Machine discovery complete machine.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="args"></param>
        void netHelper_MachineDiscoveryComplete(object sender, EventArgs args)
        {
            disDB.CloseConnection();
            disDB = null; 
            if ( MachineDiscoveryComplete!=null)
            {
                MachineDiscoveryComplete(this, null); 
            }
        }

        /// <summary>
        /// Callback for domain discovery start event.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="args"></param>
        void netHelper_DomainDiscoveryStart(object sender, DiscoveryDetailsArgs args)
        {
            if (DomainDiscoveryStart != null)
            {
                DomainDiscoveryStart(this, null);
            }
        }


        /// <summary>
        /// Callback called when the domain found event is raised by nethelper
        /// </summary>
        /// <param name="sender">nethelper object</param>
        /// <param name="args">DomainDetailsArgs class contains the domain details</param>
        void netHelper_DomainFound(object sender, DomainDetailsArgs args)
        {
            if (DomainFound != null)
            {
                DomainFound(this, args);
            }
        }

        /// <summary>
        /// Domanin discovery complete
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="args"></param>
        void netHelper_DomainDiscoveryComplete(object sender, EventArgs args)
        {
            if (DomainDiscoveryComplete != null)
            {
                DomainDiscoveryComplete(this, null);
            }
        }

        /// <summary>
        /// function is called when (primarily the UI) needs to update the lists 
        /// of discovered machines
        /// </summary>
        public void UpdateMachineList()
        {
            //WorkRequestDelegate workItem = new WorkRequestDelegate(UpdateFromDatabase);
            //tp1.PostRequest(workItem);
        }

        /// <summary>
        /// Private thread function that retrieves the machine list from database and 
        /// then fires discovery start machine found and discovery complete events
        /// </summary>
        public List<EntDiscover>UpdateFromDatabase(string machineName)
        {
            DiscoverDb _tempDb = new DiscoverDb();

            List<EntDiscover> discMachineList = _tempDb.GetDiscover(machineName);

            _tempDb.CloseConnection();
            _tempDb = null; 

            return discMachineList;
        }

        /// <summary>
        /// Thread function to collect the wmi data from machine.
        /// </summary>
        /// <param name="hostName">name of the machine</param>
        public void ScanMachine (string hostName)
        {
            _logger.Info("retrieve the wmi data from machine "+hostName);
            ThreadDelegate cb2 = new ThreadDelegate(CollectWMIData);
            object[]args={hostName};
            tp1.PostRequest(cb2, args);
        }

        /// <summary>
        /// Thread function to collect wmi data
        /// </summary>
        /// <param name="param">receives the machine name or ip for data collecton</param>
        private void CollectWMIData(object param)
        {
            string hostname = (string)param;
            _logger.Info("wmi data collection started for machine " +hostname);
            DiscoverDb _disDb = new DiscoverDb();
            EntDiscover machinedetails = _disDb.GetMachineDetails(hostname);

            CredentialDB _credDb = new CredentialDB();
            EntCredential _cred = _credDb.GetCredential(machinedetails.CredentialID);
            _credDb.CloseConnection();
            _credDb = null;

            string _UserName = null;
            string _Password = null; 

            if ( _cred != null)
            {
                _UserName = _cred.Username;
                _Password = _cred.Password; 
            }

            FireStatusMessage("Collecting wmi data for "+hostname); 
            WMIHandler objHandler = new WMIHandler(machinedetails.MachineName, _UserName, _Password);
            EntScan scan = new EntScan();

            if ( objHandler.IsWmiInitialized == true )
            {

                scan.MachineID = machinedetails.MachineID;

                ScanDb _scandb = new ScanDb();
                int scanID = _scandb.InsertScan(scan);
                _scandb.CloseConnection();
                _scandb = null;

                EntityDb entityDb = new EntityDb();

                FireStatusMessage(hostname+" : Collecting processor information");

                foreach (EntProcessor pro in objHandler.GetProcessorDetails())
                {
                    pro.ScanID = scanID;
                    //procDb.InsertProcessor(pro);
                    entityDb.Insert(pro);
                }

               FireStatusMessage(hostname+" : Collecting OS information"); 

                foreach (EntOS os in objHandler.GetOSDetails())
                {
                    os.ScanID = scanID;
                    //osDb.InsertOS(os);
                    entityDb.Insert(os);
                }

               FireStatusMessage(hostname+" : Collecting bios information"); 

               foreach (EntBios bios in objHandler.GetBiosDetails())
               {
                   bios.ScanID = scanID;
                   entityDb.Insert(bios); 
               }

               FireStatusMessage(hostname+" : Collecting Motherboard information");

               foreach (EntMotherBoard motherBd in objHandler.GetMotherBoardDetails())
               {
                   motherBd.ScanID = scanID;
                   entityDb.Insert(motherBd); 
               }

               FireStatusMessage(hostname+" : Collecting Disk information");

               foreach (EntDisk disk in objHandler.GetDiskDetails())
               {
                   disk.ScanID = scanID;
                   entityDb.Insert(disk);
               }

               FireStatusMessage(hostname+" : Collecting Memory information");

               foreach (EntMemory mem in objHandler.GetMemoryDetails())
               {
                   mem.ScanID = scanID;
                   entityDb.Insert(mem);
               }

               FireStatusMessage(hostname+" : Collecting Logical Drive information");

               foreach (EntLogicalDrive ldrive in objHandler.GetLogicalDriveDetails())
               {
                   ldrive.ScanID = scanID;
                   entityDb.Insert(ldrive);
               }

               FireStatusMessage(hostname+" : Collecting CDRom Drive information");

               foreach (EntCDRom lCDRom in objHandler.GetCDRomDetails())
               {
                   lCDRom.ScanID = scanID;
                   entityDb.Insert(lCDRom);
               }

               FireStatusMessage(hostname+" : Collecting Video information");

               foreach (EntVideo lVideo in objHandler.GetVideoDetails())
               {
                   lVideo.ScanID = scanID;
                   entityDb.Insert(lVideo);
               }

               FireStatusMessage(hostname+" : Collecting Multimedia information");

               foreach (EntMultimedia lMultimedia in objHandler.GetMultimediaDetails())
               {
                   lMultimedia.ScanID = scanID;
                   entityDb.Insert(lMultimedia);
               }


               FireStatusMessage(hostname+" : Collecting Monitor information");

               foreach (EntMonitor lMonitor in objHandler.GetMonitorDetails())
               {
                   lMonitor.ScanID = scanID;
                   entityDb.Insert(lMonitor);
               }

               FireStatusMessage(hostname+" : Collecting Share information");

               foreach (EntShare lShare in objHandler.GetShareDetails())
               {
                   lShare.ScanID = scanID;
                   entityDb.Insert(lShare);
               }

               FireStatusMessage(hostname+" : Collecting StartUp information");

               foreach (EntStartUp lStartUp in objHandler.GetStartUpDetails())
               {
                   lStartUp.ScanID = scanID;
                    entityDb.Insert(lStartUp);
               }

               FireStatusMessage(hostname+" : Collecting Hotfix information");

               foreach (EntHotfixes lHotfix in objHandler.GetHotfixDetails())
               {
                   lHotfix.ScanID = scanID;
                   entityDb.Insert(lHotfix);
               }

               FireStatusMessage(hostname+" : Collecting Process information");

               foreach (EntProcesses lProcesses in objHandler.GetProcessesDetails())
               {
                   lProcesses.ScanID = scanID;
                   entityDb.Insert(lProcesses);
               }

               FireStatusMessage(hostname+" : Collecting installed software information");

               foreach (EntSoftwares lSoftware in objHandler.GetInstalledSoftwareDetails())
               {
                   lSoftware.ScanID = scanID;
                   entityDb.Insert(lSoftware); 

               }

               FireStatusMessage(hostname+" : Collecting service information");

               foreach (EntServices lServices in objHandler.GetServicesDetails())
               {
                   lServices.ScanID = scanID;
                   entityDb.Insert(lServices);
               }

               FireStatusMessage(hostname+" : Collecting IPRoutes information");

               foreach (EntIPRoutes lIPRoutes in objHandler.GetIPRoutes())
               {
                   lIPRoutes.ScanID = scanID;
                   entityDb.Insert(lIPRoutes);
               }

               FireStatusMessage(hostname+" : Collecting Environment variable information");

               foreach (EntEnvironmentVars lEnvVar in objHandler.GetEnvironmentVariables())
               {
                   lEnvVar.ScanID = scanID;
                   entityDb.Insert(lEnvVar);
               }

               FireStatusMessage(hostname+" : Collecting Computer information");

               foreach (EntComputer lComp in objHandler.GetComputerDetails())
               {
                   lComp.ScanID = scanID;
                   entityDb.Insert(lComp);
               }

               FireStatusMessage(hostname+" : Collecting Printer information");

               foreach (EntPrinter lPrinter in objHandler.GetPrinterDetails())
               {
                   lPrinter.ScanID = scanID;
                   entityDb.Insert(lPrinter);
               }

               FireStatusMessage(hostname+" : Collecting UserGroups information");

               foreach (EntUserGroups lUserGroups in objHandler.GetUserGroupDetails())
               {
                   lUserGroups.ScanID = scanID;
                   entityDb.Insert(lUserGroups);
               }

                entityDb.CloseConnection();
                entityDb = null; 

                _disDb.UpdateLastScan(scan.MachineID, scan.Date);

                machinedetails.Discovered = true;
                machinedetails.StatusMessage = "Scan successful"; 

            }
            else
            {
                _logger.Error("WMI not initialized for machine <" + hostname+">");
                machinedetails.StatusMessage = "wmi initialization failure"; 
                machinedetails.Discovered = false;
            }

            machinedetails.LastChecked = DateTime.Now; 
            _disDb.UpdateDiscover(machinedetails); 

            if ( ScanComplete!=null)
            {
                ScanComplete(this, machinedetails.MachineName);
            }
            FireStatusMessage("Scan complete"); 
            _disDb.CloseConnection();
            _disDb = null; 

        }

        public List<EntCredential> GetCredentials()
        {
            List<EntCredential> lstCred = null;
            CredentialDB _credDb = new CredentialDB();
            lstCred = _credDb.GetAllCredentials();
            _credDb.CloseConnection();
            _credDb = null;

            return lstCred; 
        }

        public void SetCredentials(List<EntCredential> lstCredentials)
        {
            CredentialDB _credDb = new CredentialDB();
            _credDb.UpdateCredentials(lstCredentials);
            _credDb.CloseConnection();
            _credDb = null; 
        }

        //TODO_HIGH : return the collection of the objects to the UI
        public int GetWmiDataFromDB(string machineName, DateTime scanDate, ref Hashtable objDataTable)
        {
            
            //TODO_NORM: Improve the code below so db handling is proper.
            DiscoverDb discDb = new DiscoverDb();
            EntDiscover machineDetails = discDb.GetMachineDetails(machineName);
            discDb.CloseConnection();
            discDb = null; 

//TODO_HIGH: Code only displays the latest, should display scan selected using combobox
            ScanDb _scanDb = new ScanDb();
            EntScan scan = _scanDb.GetLatestScanDetails(machineDetails.MachineID, scanDate);
            _scanDb.CloseConnection();
            _scanDb = null; 

            if ( scan != null)
            {
                EntityDb entityDb = new EntityDb();

                ArrayList sysData = new ArrayList();
                ArrayList.Synchronized(sysData);
                entityDb.GetEntity(scan.ScanID, EntityType.Computer, ref sysData);
                entityDb.GetEntity(scan.ScanID, EntityType.Processor, ref sysData); 
                entityDb.GetEntity(scan.ScanID, EntityType.OS, ref sysData);
                entityDb.GetEntity(scan.ScanID, EntityType.Bios, ref sysData);
                entityDb.GetEntity(scan.ScanID, EntityType.MotherBoard, ref sysData);
                entityDb.GetEntity(scan.ScanID, EntityType.Disk, ref sysData);
                entityDb.GetEntity(scan.ScanID, EntityType.Memory, ref sysData);
                entityDb.GetEntity(scan.ScanID, EntityType.LogicalDrive, ref sysData);
                entityDb.GetEntity(scan.ScanID, EntityType.CDRom, ref sysData);
                entityDb.GetEntity(scan.ScanID, EntityType.Video, ref sysData);
                entityDb.GetEntity(scan.ScanID, EntityType.Multimedia, ref sysData);
                entityDb.GetEntity(scan.ScanID, EntityType.Monitor, ref sysData);
                entityDb.GetEntity(scan.ScanID, EntityType.Share, ref sysData);
                entityDb.GetEntity(scan.ScanID, EntityType.Printer, ref sysData);
                entityDb.GetEntity(scan.ScanID, EntityType.Hotfix, ref sysData);
                entityDb.GetEntity(scan.ScanID, EntityType.StartUp, ref sysData);
                objDataTable.Add("System", sysData);

                ArrayList processData = new ArrayList();
                ArrayList.Synchronized(processData); 
                entityDb.GetEntity(scan.ScanID, EntityType.Processes, ref processData);
                objDataTable.Add("Processes", processData);

                ArrayList softwareData = new ArrayList();
                ArrayList.Synchronized(softwareData);
                entityDb.GetEntity(scan.ScanID, EntityType.Softwares, ref softwareData);
                objDataTable.Add("Softwares", softwareData);

                ArrayList servicesData = new ArrayList();
                ArrayList.Synchronized(servicesData);
                entityDb.GetEntity(scan.ScanID, EntityType.Services, ref servicesData);
                objDataTable.Add("Services", servicesData);

                ArrayList ipRoutesData = new ArrayList();
                ArrayList.Synchronized(ipRoutesData);
                entityDb.GetEntity(scan.ScanID, EntityType.IPRoutes, ref ipRoutesData);
                objDataTable.Add("IPRoutes", ipRoutesData);

                ArrayList ipEnvVars = new ArrayList();
                ArrayList.Synchronized(ipEnvVars);
                entityDb.GetEntity(scan.ScanID, EntityType.EnvironmentVar, ref ipEnvVars);
                objDataTable.Add("EnvVars", ipEnvVars);

                ArrayList userGrps = new ArrayList();
                ArrayList.Synchronized(userGrps);
                entityDb.GetEntity(scan.ScanID, EntityType.UserGroup, ref userGrps);
                objDataTable.Add("UserGroups", userGrps); 

                entityDb.CloseConnection();
                entityDb = null; 
            }

            return 1; 
        }

        private void FireStatusMessage(string message)
        {
            if ( StatusMessage!=null)
            {
                _logger.Info(message);
                StatusMessage(this, message); 
            }
        }

        public List<EntScan> GetAllScans(string machineName)
        {
            List<EntScan> retList = null; 
            DiscoverDb discDb = new DiscoverDb();
            EntDiscover machine = discDb.GetMachineDetails(machineName);
            discDb.CloseConnection();
            discDb = null; 
            ScanDb scanDb = new ScanDb();
            retList = scanDb.GetScanDetails(machine.MachineID); 
            scanDb.CloseConnection(); 
            scanDb = null;
            return retList; 
        }

        public void DeleteMachine(string machineName)
        {
            DiscoverDb discDb = new DiscoverDb();
            discDb.DeleteDiscover(machineName);
            discDb.CloseConnection();
            discDb = null; 

        }

        public List<EntDiscover>GetMachineList()
        {
            List<EntDiscover> discover = new List<EntDiscover>(); 
            DiscoverDb discDb = new DiscoverDb();
            discover = discDb.GetDiscover(null); 
            discDb.CloseConnection();
            discDb = null;

            return discover; 

        }

        public void UpdateDiscoveryMachines(List<EntDiscover> lstDiscover)
        {
            DiscoverDb discoveryDb = new DiscoverDb();
            discoveryDb.UpdateDiscover(lstDiscover);
            discoveryDb.CloseConnection();
            discoveryDb = null; 
        }

        public EntScan GetLatestScan(int machineID)
        {
            EntScan ret  = null; 
            ScanDb scan= new ScanDb(); 
            ret = scan.GetLatestScanDetails(machineID) ;
            scan.CloseConnection();
            scan = null; 
            return ret; 

        }
    }
}
